<?php
class InteractiveReservation
{
    const CACHE_ALL_TABLES_KEY = "tables_all";
    const CACHE_TABLE_ID_PREFIX = "table_";
    const CACHE_SEAT_ID_PREFIX = "seat_";
    const CACHE_USER_ID_PREFIX = "user_";

    private $_memcache;

    public function __construct(){
        $this->_memcache = new myMemcache();
    }

    private function CheckMemcache(){
        if (!$this->_memcache->getState()){
            throw new Exception(MESSAGE_MEMCACHE_DOWN);
        }
    }

    public function Save($tables){
        $this->CheckMemcache();
        $this->_memcache->flush();

        $allTables = array();
        foreach($tables as $table){
            $id = $table["id"];
            $allTables[] = $id;
            $seats = $table["seats"];
            $this->_memcache->set(static::CACHE_TABLE_ID_PREFIX . $id, $seats);
        }
        $this->_memcache->set(static::CACHE_ALL_TABLES_KEY, $allTables);
    }

    public function GetReservedSeats(){
        $this->CheckMemcache();
        $result = array();
        $tables = $this->GetAllTables();
        if(!$tables){
            $tables = array();
        }
        foreach($tables as $tableId){
            $table = $this->GetTable($tableId);
            if ($table){
                foreach($table as $seatId){
                    $seat = $this->GetSeat($seatId);
                    if (!$this->IsSeatAvailable($seat)){
                        $result[] = $seatId;
                    }
                }
            }
        }
        return $result;
    }


    public function TryReserveTable($code = false, $forceReserve = false){
        $count = $this->GetAvailableReservationCountForUser();
        if (!$count){
            throw new Exception(MESSAGE_RESERVATION_LIMIT_EXCEEDED);
        }

        $this->CheckMemcache();
        if (!$code){
            $tables = $this->GetAllTables();
            foreach($tables as $tableId){
                $success = $this->TryReserveTable($tableId);
                if ($success){
                    return $success;
                }
            }
            throw new Exception(MESSAGE_NO_FREE_TABLE);
        } else {
            $table = $this->GetTable($code);
            $free = array();
            if (!$table){
                $table = array();
            }
            foreach($table as $seatId){
                $seat = $this->GetSeat($seatId);
                if ($this->IsSeatAvailable($seat)){
                    $free[] = $seatId;
                }
            }
            if ((count($free) >= $count) || $forceReserve){
                if ((count($free) < $count) && $forceReserve){
                    $count = count($free);
                }
                $success = true;
                for($i = 0; $i < $count; $i++){
                    $success &= $this->TryReserveSeat($free[$i]);
                }
                if (!$success){
                    foreach($free as $seatId){
                        $this->TryUnreserveSeat($seatId);
                    }
                    throw new Exception(MESSAGE_NOT_ALL_SUCCESS);
                }
                return $success;
            }
        }
        return false;
    }

    public function TryUnReserveTable($code){
        //TODO: function
        throw new Exception("Not implemented");
    }

    public function Order(){
        $email_data = array();
        $email_data["code"] = $order_id = time() . $this->GetUserId();
        $seats = $this->GetUserSeats();
        $badReservations = 0;
        if ($seats){
            $m_order = new m_order();
            $m_order->created = time();
            $m_order->id = $order_id;
            $m_order->paid = 0;
            $m_order->user_id = $this->GetUserId();
            $email_row = array();
            if ($m_order->insert()){
                foreach($seats as $seatId){
                    $m_seat = new m_seat();
                    $m_seat->order_id = $order_id;
                    $m_seat->seat = $seatId;
                    $seatData = $this->GetSeat($seatId);
                    if ($seatData["ordered"]){
                        continue;
                    }
                    if ($m_seat->insert()){
                        $email_row["seat_id"] = $seatId;
                        $email_data["seats"][] = $email_row;
                        $seatData["ordered"] = true;
                        $this->SetSeat($seatId, $seatData);
                    } else {
                        $badReservations++;
                    }
                }
                if ($badReservations >= count($seats)){
                    $m_order->delete();
                    throw new Exception(MESSAGE_ORDER_FAILED);
                }

                $user_email = $_SESSION["user"]["email"];

                $my_smarty = new my_smarty();
                $my_smarty->assign("data", $email_data);
                $message["subject"] = "=?ISO-8859-1?Q?Rezerv=E1cia_na_Be=E1niu_FMFI?=";
                $message["message"] = $my_smarty->fetch('../'.TEMPLATE_DIR."snippets/order_email.tpl");

                general::send_mail($user_email, $message);
            } else {
                throw new Exception(MESSAGE_FAILED);
            }
            if ($badReservations){
                $m_order->comment = MESSAGE_NOT_ALL_SUCCESS . ":" . $badReservations;
                $m_order->update();
            }
            return true;
        } else {
            throw new Exception(MESSAGE_EMPTY_RESERVATION);
        }
    }


    public function TryReserveSeat($code){
        if (!$this->GetAvailableReservationCountForUser()){
            throw new Exception(MESSAGE_RESERVATION_LIMIT_EXCEEDED);
        }

        $this->CheckMemcache();
        $seat = $this->GetSeat($code);
        if ($this->IsSeatAvailable($seat)){
            $seat = array();
            $seat["isFree"] = false;
            $seat["user"] = $this->GetUserId();
            $seat["time"] = time();
            $seat["ordered"] = false;
            $this->SetSeat($code, $seat);

            $userSeats = $this->GetUserSeats();
            if (!$userSeats){
                $userSeats = array();
            }
            $userSeats[] = $code;
            $this->SetUserSeats($userSeats);

            return true;
        } else {
            throw new Exception(MESSAGE_ALREADY_TAKEN);
        }
    }

    public function TryUnreserveSeat($code){
        $this->CheckMemcache();
        $seat = $this->GetSeat($code);
        if ($seat && $seat["user"] == $this->GetUserId()){
            $seat["isFree"] = true;
            $seat["user"] = false;
            $seat["time"] = false;
            $seat["ordered"] = false;
            $this->SetSeat($code, $seat);
            $userSeats = $this->GetUserSeats();
            if ($userSeats){
                $newSeats = array();
                foreach($userSeats as $seatId){
                    if($seatId != $code){
                        $newSeats[] = $seatId;
                    }
                }
                $this->SetUserSeats($newSeats);
            }
            return true;
        } else {
            throw new Exception(MESSAGE_NOT_YOUR_RESERVATION);
        }
    }

    public function GetUserSeats(){
        $userSeats = $this->_memcache->get(static::CACHE_USER_ID_PREFIX . $this->GetUserId());
        $result = array();
        if ($userSeats){
            foreach($userSeats as $seatId){
                $seat = $this->GetSeat($seatId);
                if ($seat["user"] == $this->GetUserId()){
                    $result[] = $seatId;
                }
            }
        }
        return $result;
    }

    public function GetUserOrderedSeats(){
        $userSeats = $this->_memcache->get(static::CACHE_USER_ID_PREFIX . $this->GetUserId());
        $result = array();
        if (is_array($userSeats)){
            foreach($userSeats as $seatId){
                $seat = $this->GetSeat($seatId);
                if ($seat["user"] == $this->GetUserId() && $seat["ordered"]){
                    $result[] = $seatId;
                }
            }
        }
        return $result;
    }

    public function GetAllTables(){
        return $this->_memcache->get(static::CACHE_ALL_TABLES_KEY);
    }

    public function GetTable($code){
        return $this->_memcache->get(static::CACHE_TABLE_ID_PREFIX . $code);
    }

    private function GetSeat($code){
        return $this->_memcache->get(static::CACHE_SEAT_ID_PREFIX . $code);
    }

    private function SetSeat($code, $seat){
        return $this->_memcache->set(static::CACHE_SEAT_ID_PREFIX . $code, $seat);
    }

    private function SetUserSeats($seats){
        return $this->_memcache->set(static::CACHE_USER_ID_PREFIX . $this->GetUserId(), $seats);
    }

    private function IsSeatAvailable($seat){
        return (!$seat || (!$seat["ordered"] && ($seat["isFree"] || $seat["time"] + RESERVATION_TIME_LIMIT < time())));
    }

    private function GetUserId(){
        return $_SESSION["user"]["id"];
    }

    public function GetAvailableReservationCountForUser()
    {
        $reservedSeats = $this->GetUserSeats();
        $count = 0;
        if ($reservedSeats){
            $count = count($reservedSeats);
        }
        $max_seats = ($_SESSION["user"]["security_level"] >= SECURITY_LEVEL_ADMIN) ? 999 : MAX_SEATS_PER_USER;
        return $max_seats - $count;
    }
}